package com.corgi.repeat.aspect;

import com.corgi.formatter.ProtoBufFormatter;
import com.corgi.repeat.annotation.Repeat;
import com.corgi.repeat.core.RepeatLock;
import com.corgi.repeat.handler.CurrentRepeatAspectHandler;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.concurrent.TimeUnit;

/**
 * @title: RepeatAspect
 * @description:
 * @author: dengmiao
 * @create: 2019-11-19 11:22
 **/
@Slf4j
@Component
@Aspect
@ConditionalOnProperty(prefix = "current.repeat", name = "part-enabled", havingValue = "true", matchIfMissing = true)
public class RepeatAspect {

    private final static Object PRESENT = new Object();

    @Autowired(required = false)
    private CurrentRepeatAspectHandler handler;

    @Around("@annotation(com.corgi.repeat.annotation.Repeat)")
    public Object handleResubmit(ProceedingJoinPoint joinPoint) throws Throwable {
        Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
        //获取注解信息
        Repeat repeat = method.getAnnotation(Repeat.class);
        int delay = repeat.delay();
        TimeUnit timeUnit = repeat.timeUnit();
        Object[] pointArgs = joinPoint.getArgs();
        ProtoBufFormatter formatter = new ProtoBufFormatter();

        // 理应带上客户端唯一标识 如ip
        byte[] ser = formatter.format(method.toGenericString());
        for (Object arg : pointArgs) {
            byte[] bytes = formatter.format(arg);
            ser = ProtoBufFormatter.byteMergerAll(ser, bytes);
        }
        String key = RepeatLock.handleKey(ser);
        //执行锁
        boolean lock = false;
        RepeatLock repeatLock = RepeatLock.getInstance();
        try {
            //设置解锁key
            lock = repeatLock.lock(key, PRESENT);
            if (lock || handler == null) {
                //放行
                return joinPoint.proceed();
            } else {
                //响应重复提交异常
                return handler.around(joinPoint, repeat);
            }
        } finally {
            //设置解锁key和解锁时间
            repeatLock.unLock(lock, key, delay, timeUnit);
        }
    }
}
